
#include "VoiceRecoSession.h"
#include "StreamAdapters.h"
#include "Tasks.h"

#include "qisr.h"
#include "Utility/IniReader.h"

using namespace std;
using namespace nebula::utility;

namespace smartvoice {

//////////////////////////////////////////////////////////////////////////
VoiceSessionManager::VoiceSessionManager()
{
}

VoiceSessionManager::~VoiceSessionManager()
{
}

NEBULA_ERROR_CODE VoiceSessionManager::LoadConfig(const char* config_path)
{
	IniReader ini;
	if (!ini.parse(config_path))
		return NEBULA_ERR_INVALID_ARGUMENT;

	vector<string> sections;
	ini.get_sections(sections);

	for (vector<string>::iterator i = sections.begin();
		i != sections.end(); ++i)
	{
		vector<string> names;
		ini.get_names(*i, names);

		if (*i == "config")
		{
			for (vector<string>::iterator j = names.begin();
				j != names.end(); ++j)
			{
				SetConfig((*j).c_str(), ini.get_str(*i, *j).c_str());
			}	
		}
		else
		{
			for (vector<string>::iterator j = names.begin();
				j != names.end(); ++j)
			{
				SetSessionTemplate((*i).c_str(), (*j).c_str(), ini.get_str(*i, *j).c_str());
			}	
		}
	}

	return NEBULA_ERR_SUCCESS;	
}

NEBULA_ERROR_CODE VoiceSessionManager::SetConfig(const char* key, const char* value)
{
	_config_map[key] = value;
	return NEBULA_ERR_SUCCESS;
}

NEBULA_ERROR_CODE VoiceSessionManager::DelConfig(const char* key)
{
	str2str_map::iterator i = _config_map.find(key);
	if (i != _config_map.end())
		_config_map.erase(i);

	return NEBULA_ERR_SUCCESS;
}

NEBULA_ERROR_CODE VoiceSessionManager::SetSessionTemplate(const char* name, const char* field, const char* val)
{
	_template_map[name][field] = val;
	return NEBULA_ERR_SUCCESS;
}

NEBULA_ERROR_CODE VoiceSessionManager::DelSessionTemplate(const char* name)
{
	nested_str2str_map::iterator i = _template_map.find(name);
	if (i != _template_map.end())
		_template_map.erase(i);
	return NEBULA_ERR_SUCCESS;
}

NEBULA_ERROR_CODE VoiceSessionManager::InitEnv()
{
	string conf = ComposeConfStr(_config_map);
	int ret = QISRInit(conf.c_str());
	if (ret != 0)
		return NEBULA_ERR_SVR_INITIALIZING;
	return NEBULA_ERR_SUCCESS;
}

VoiceRecoSession* VoiceSessionManager::AllocSession(const char* templatename)
{
	nested_str2str_map::iterator i = _template_map.find(templatename);
	if (i == _template_map.end())
		return 0;

	string conf = ComposeConfStr((*i).second);
	long   fragsize = 16 * 1024;
	return new VoiceRecoSession(fragsize, conf, "");
}

void VoiceSessionManager::CloseSession(VoiceRecoSession* psession)
{
	delete psession;
}

std::string VoiceSessionManager::ComposeConfStr(str2str_map& keypairs)
{
	string ret;
	for (str2str_map::iterator i = keypairs.begin();
		i != keypairs.end(); ++i)
	{
		string tmp = (*i).first + "=" + (*i).second;
		ret += tmp;
		ret += ",";
	}

	return ret;
}

//////////////////////////////////////////////////////////////////////////

VoiceRecoSession::VoiceRecoSession(
	const long fragsize, 
	const std::string& conf, 
	const std::string& grammar
	) :
	_session_id(0),
	_fragment_size(fragsize),
	_config(conf),
	_grammar(grammar)
{
}

VoiceRecoSession::~VoiceRecoSession()
{

}

NEBULA_ERROR_CODE VoiceRecoSession::CommitTask(ITask* ptask)
{
	SessionTask* rawtask = dynamic_cast<SessionTask*>(ptask);
	if (rawtask == 0)
		return NEBULA_ERR_INVALID_ARGUMENT;

	NEBULA_ERROR_CODE rc = NEBULA_ERR_SUCCESS;

	if ((rc = BeginSession()) != NEBULA_ERR_SUCCESS)
		return rc;

	if ((rc = WriteData(rawtask->m_audio.c_str(), rawtask->m_audio.size())) != NEBULA_ERR_SUCCESS)
		return rc;	

	if ((rc = GetResult(rawtask->m_text)) != NEBULA_ERR_SUCCESS)
		return rc;

	if ((rc = EndSession()) != NEBULA_ERR_SUCCESS)
		return rc;

	return NEBULA_ERR_SUCCESS;
}

NEBULA_ERROR_CODE VoiceRecoSession::BeginSession()
{	
	int err_code = 0;
	_session_id = QISRSessionBegin("", _config.c_str(), &err_code);
	if (err_code != 0)
	{
		_session_id = 0;
		return NEBULA_ERR_SYSTEM_API_FAILURE;
	}
	return NEBULA_ERR_SUCCESS;
}

NEBULA_ERROR_CODE VoiceRecoSession::EndSession()
{
	if (_session_id == 0)
		return NEBULA_ERR_INVALID_ARGUMENT;
	
	int ret = QISRSessionEnd(_session_id, 0);
	if (ret != 0)
		return NEBULA_ERR_SYSTEM_API_FAILURE;

	return NEBULA_ERR_SUCCESS;
}

int VoiceRecoSession::WriteAudio(const char* data, int size, int mode)
{
	int epstatus = 0, recostatus = 0, rc = 0;

	rc = QISRAudioWrite(
		_session_id, data, size, 
		mode, 
		&epstatus, &recostatus
		);

	if (rc != 0)
		return -1;

	return size;
}

NEBULA_ERROR_CODE VoiceRecoSession::WriteData(const void* data, int size)
{
	const char* cur = static_cast<const char*>(data);
	const char* end = cur + size;

	if (end - cur <= _fragment_size)
	{
		int bytewritten = WriteAudio(cur, size, 
			ISR_AUDIO_SAMPLE_LAST | ISR_AUDIO_SAMPLE_FIRST);
		if (bytewritten == -1)
			return NEBULA_ERR_SYSTEM_API_FAILURE;

		return NEBULA_ERR_SUCCESS;
	}

	{
		int bytewritten = WriteAudio(cur, _fragment_size, 
			ISR_AUDIO_SAMPLE_CONTINUE | ISR_AUDIO_SAMPLE_FIRST);
		if (bytewritten == -1)
			return NEBULA_ERR_SYSTEM_API_FAILURE;

		cur += bytewritten;
	}

	while (end - cur > _fragment_size)
	{
		int bytewritten = WriteAudio(cur, _fragment_size, ISR_AUDIO_SAMPLE_CONTINUE);
		if (bytewritten == -1)
			return NEBULA_ERR_SYSTEM_API_FAILURE;

		cur += bytewritten;
	}

	{
		int bytewritten = WriteAudio(cur, end - cur, ISR_AUDIO_SAMPLE_LAST);
		if (bytewritten == -1)
			return NEBULA_ERR_SYSTEM_API_FAILURE;
	}
	
	return NEBULA_ERR_SUCCESS;
}

NEBULA_ERROR_CODE VoiceRecoSession::GetResult(std::string& output)
{
	int retstatus = 0, errcode = 0;
	const char* ret = QISRGetResult(_session_id, &retstatus, 0, &errcode);
	if (errcode != 0 || ret == 0 || retstatus == 1)
		return NEBULA_ERR_SYSTEM_API_FAILURE;
	
	output = ret;
	return NEBULA_ERR_SUCCESS;
}

} //end of namespace smartvoice
